(function (window) {
    window.URL = window.URL || window.webkitURL;
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
    /**
     * channels is fixed ,must and only be 1
     * sampleFormat hopefully should be 32bit float 
     * sampleRate No specific value 
     */
    var Recorder = function (stream, config) {
        var self = this;
        config = config || {}; 
        config.sampleBits = config.sampleBits || 8      
        config.sampleRate = config.sampleRate || (44100 / 6); 
        this.dataCallback = config.dataCallback ? config.dataCallback : null; 
        this.context = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext)();
        this.audioInput = this.context.createMediaStreamSource(stream);
        var createScript = this.context.createScriptProcessor || this.context.createJavaScriptNode;
        this.audioStream = stream;
        this.sampleRate = this.context.sampleRate;
        this.sampleBits = this.context.sampleBits;
        console.info("aRecorder","sampleRate=",this.sampleRate,"sampleBits=",this.sampleBits,this.context);
        //channels is must be 1
        this.recorder = createScript.apply(this.context, [4096, 1, 1]);
        this.stopFlag = true;
        var audioData = {
            /*preProcess: function(data){
                var data0 = new Float32Array(data);
                var compression = parseInt(context.sampleRate / this.outputSampleRate);
                var length = data0.length / compression;
                var result = new Float32Array(length);
                var index = 0,j = 0;
                while(index < length){
                    result[index] = data[j];
                    j += compression;
                    index ++;
                }
                //sampleBits == 16
                var dataView = new DataView(result);
                for (var i = 0; i < result.byteLength; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, result[i]));
                    dataView.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
                console.info("original:sampleRate",this.context.sampleRate,"final pcm:",result.size);
                //for test
                //return result;
                //return new Blob([data], { type: 'audio/wav' });
            }*/
        };
        this.getSampleRate = function(){
            return this.context.sampleRate;
        }
        this.getChannels = function(){
            return 1;
        }
        //start recording
        this.start = function () {
            this.stopFlag = false;
            this.audioInput.connect(this.recorder);
            this.recorder.connect(this.context.destination);
        }
        this.pause = function(){
             
        }
        this.resume = function(){
            
        }
        //stop
        this.stop = function () {
            this.stopFlag = true;
            this.audioStream.getTracks().forEach(track => track.stop());
            this.recorder.disconnect();
            this.audioInput.disconnect();
            this.audioInput.onaudioprocess = null;
        }
        //audio collection
        this.recorder.onaudioprocess = function (e) {
            if(this.stopFlag) return ;
            self.dataCallback && self.dataCallback(e.inputBuffer.getChannelData(0));
        }
    };
    //throw exceptions
    Recorder.throwError = function (message) {
        alert(message);
        throw new function () { this.toString = function () { return message; } }
    }
    //support audio recording
    Recorder.canRecording = (navigator.getUserMedia != null);
    //get audioStream
    Recorder.get = function (callback,errCallback, config) {
            if (navigator.getUserMedia) {
                navigator.getUserMedia(
                    { audio: true } 
                    , function (stream) {
                        callback && callback(new Recorder(stream, config));
                    }
                    , function (error) {
                        switch (error.code || error.name) {
                            case 'PERMISSION_DENIED':
                            case 'PermissionDeniedError':
                                errCallback &&  errCallback('PERMISSION_DENIED');
                                //Recorder.throwError('PERMISSION_DENIED');
                                break;
                            case 'NOT_SUPPORTED_ERROR':
                            case 'NotSupportedError':
                                errCallback &&  errCallback('NOT_SUPPORTED_ERROR');
                                //Recorder.throwError('NOT_SUPPORTED_ERROR');
                                break;
                            case 'MANDATORY_UNSATISFIED_ERROR':
                            case 'MandatoryUnsatisfiedError':
                                errCallback &&  errCallback('MANDATORY_UNSATISFIED_ERROR');
                                //Recorder.throwError('MANDATORY_UNSATISFIED_ERROR');
                                break;
                            default:
                                errCallback && errCallback('CAN_NOT_OPEN_MICROPHONE。INFO:' + (error.code || error.name));
                                //Recorder.throwError('CAN_NOT_OPEN_MICROPHONE。INFO:' + (error.code || error.name));
                                break;
                        }
                    });
            } else {
                errCallback && errCallback('not support audio recording');
                //Recorder.throwErr('not support audio recording'); return;
            }
    }
    
    window.SpRecorder = Recorder;
})(window);